package core

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/url"
	"sync"

	"github.com/go-pay/gopay"
	"github.com/go-pay/gopay/alipay"
	"github.com/gofiber/fiber/v2"
)

var (
	AliTemp    *Ali
	AliOnce    sync.Once
	outTradeNo = "GZ201901301040355718"
	AliCli     *alipay.Client
)

const (
	AliAppid        = "9021000133664519"
	AliPayPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6PDj25tftKcAlSbxZNcFkC0KYpWBq5Qb33en75vnshzJEVzwMHdMD3wkoCWYMsvdlqbmRR04oDd2W3R2vBtHIOXd//KzyTuF0wyHdLzJ1UrOGwh5d6yzdqV/c5Q3DjFo26nnohxcLlps4MADP3WEUCw244vIYB2/1erH4MA5bZ6h+B5aY6ehO71ZjoGPa7E68cmzwNuTJ0PLNaCBOHTPiZQxU+TBBQ3nNWk9nC2KnVB56L2vItwOgnrx7VKSC5kCZ21qE603/krnmL+aXn1Vx7a1APYf0+K7QccQetLsh9v2LWsNE6MPMXAqdTX1ZYDxb5CjxJy41NDKSkRtc8cfBQIDAQAB"
	AliPrivateKey   = "MIIEogIBAAKCAQEAjXo7T5U4cdz1+k88OsTJhDHqAltnOspTa3hOPtwDPltvCvM6bvlx+M2ltFptheSbWHoWrFTs8I8HTR8GRNkgjD1mQ1YwrMA4fm5u0LK935g85ssMMvBoYpSVJ3Rwd8VfBVCLhLb81uju5KRFCmGn2xOH+kaWZvGmmym2kgFs65Q+9OvOFYehjXuxASreFlWkCha+0LM/llck0vm5C55XBqsCAvI4aPWfG1Inay5G2wf+4TvSQKiHnKqR7mnRx3gxNWn8vXvLg2CDwKAiHG+KcGkfS/zGD3xb0lerz7shzNWEVYvhYnEzGGn0ephyY7Dxk3rRD6QLlI3WzBFJRL1wWQIDAQABAoIBABGwUK4YmCAUYmTTd1JZvGBc5Uc6KmP9XAS52/6b/4PSZsXVt16crZVQEQHFtJ/i/1FGooMVczJ4j48i6+O7ygf+vUR6CEfxGJ8mK5DIaFkGbcblaDcOO97neOaf9lfjQ4gkk+0Z++7a3NOVflRX9Wa8wGmYXcGtQQlxY7RJnVau2L9L9gGbMiHz7YdaFaVj/FABMtpTMwaVyIQWKzyAgOGXcCLZHR5J0AqZvhCX7clIQdaFiOSsRDlz23MWJp7va+wDbG5wHtgVn3lVulK4e7Y4cd9QOYJm5tNlquWgmja4IBWidXLfKXUiNgnt0hbkXx1qh90+OJVyt0gOCWi9ickCgYEAwd8z2+sqqVocWBAzAgK9HBEy3U+uY4IYs604TGdZIR1yzLG0fcg4HSKgOGyY+SDNgI0reStLQvZTcpWtPedIEJQT7T6/o7btE93Lo3bSp0qshSIVA6RRHAt2+ygozkuWODkU2y4qVBmk7Fo10HvG5RJquVjwlN+mvHPsa1RS/ZMCgYEAutC2l9N4yI0pQHhCf22V2oPPG/EtbrzNb2bn36e3E7qYkXkxO611Q9lFoHDONyHzqcsd7Op2q+Iiu2du2xMMm/4lfvjgG0uXwSYWrT8c17DSqAr46sOiboGRE0cMXSYSSH6cizFSK6edIT2x7M2IApNhyF14W8k7bzysCcd1beMCgYAaeRUGwaz4pRJbwBKHAsK582tDpx7D/b7Lwip+3FniWLgVVWq1BkMLT3AbZFDbK+yD0kMOb6f+Q86PRfr7CfrgMKgr7DaU72SeItosvwvyxhKat1WxQp3Eu5gKoag4yz9+++RqLXqgM0Q+9e6FQnGdVA94sblgSs3ky9uLqF8QuwKBgH1q7YrUYDxT+KPdmNcv3PEMmJiru9KiUlE+BuFyGUjXduIDOPhVpWpdjY7+7JK66njNclPBvNX8RVEHSbct/MMlRksODnaEs2bqiULaIiT0Axjp9N63ZG9H8Pu+MSDAFHoy9inEfOPt7WGJp8/Q1ETWaUk45zrHyG0PkHgoPj/9AoGAPp7FcNZsp7DXjVzCvqslZpi2CFHsfzLgm5MhboLGO87Eq3Ef99Lm3QgiGPuatS7MHdNHPQDn2nlYicTNEZtWjyzewZoKTZJ/sEsLi/je0KecyflYb7OCiPBE6+z1Qy7ObfRoAfV3XDU4yAb4NLn/54hYQCe00GDsYXBsyo5mOm4="
	isProd          = false
)

func init() {
	client, err := alipay.NewClient(AliAppid, AliPrivateKey, isProd)
	if err != nil {
		log.Panicln(err)
		return
	}

	// 自定义配置http请求接收返回结果body大小，默认 10MB
	// client.SetBodySize() // 没有特殊需求，可忽略此配置

	// 打开Debug开关，输出日志，默认关闭
	// client.DebugSwitch = gopay.DebugOn

	// 设置支付宝请求 公共参数
	// 注意：具体设置哪些参数，根据不同的方法而不同，此处列举出所有设置参数
	client.SetLocation(alipay.LocationShanghai). // 设置时区，不设置或出错均为默认服务器时间
							SetCharset(alipay.UTF8).                                             // 设置字符编码，不设置默认 utf-8
							SetSignType(alipay.RSA2).                                            // 设置签名类型，不设置默认 RSA2
							SetReturnUrl("https://www.fmm.ink").                                 // 设置返回URL
							SetNotifyUrl("https://mungo.mynatapp.cc/api/public/Alipay/callback") // 设置异步通知URL
	// 设置biz_content加密KEY，设置此参数默认开启加密
	// client.SetAESKey("1234567890123456")

	// 自动同步验签（只支持证书模式）
	// 传入 AlipayPublicCert.crt 内容
	// client.AutoVerifySign([]byte("AlipayPublicCert.crt bytes"))

	// 公钥证书模式，需要传入证书，以下两种方式二选一
	// 证书路径 1
	// err = client.SetCertSnByPath("appPublicCert.crt", "AlipayRootCert.crt", "AlipayPublicCert.crt")
	// 证书内容 2
	// err = client.SetCertSnByContent("appPublicCert.crt bytes", "AlipayRootCert bytes", "AlipayPublicCert.crt bytes")
	AliCli = client
}

type Ali struct{}

func NewAli() *Ali {
	AliOnce.Do(func() {
		AliTemp = &Ali{}
	})
	return AliTemp
}

// Callback 支付回调处理
func (m *Ali) Callback(params url.Values) (outTradeNo string, jsonStr string, err error) {
	notifyReq := make(gopay.BodyMap, len(params)+1)
	for k, v := range params {
		if len(v) == 1 {
			notifyReq.Set(k, v[0])
		}
	}

	// 支付宝异步通知验签（公钥模式）
	ok, err := alipay.VerifySign(AliPayPublicKey, notifyReq)
	if err != nil {
		return outTradeNo, jsonStr, errors.New("Alipay验签异常:" + err.Error())
	}
	if !ok {
		return outTradeNo, jsonStr, errors.New("验签失败")
	}

	// 支付宝异步通知验签（公钥证书模式）
	// ok, err = Alipay.VerifySignWithCert("AlipayPublicCert.crt content", notifyReq)

	// 如果需要，可将 BodyMap 内数据，Unmarshal 到指定结构体指针 ptr
	// err = notifyReq.Unmarshal(ptr)

	// ====异步通知，返回支付宝平台的信息====
	// 文档：https://opendocs.Alipay.com/open/203/105286
	// 程序执行完后必须打印输出“success”（不包含引号）。如果商户反馈给支付宝的字符不是success这7个字符，支付宝服务器会不断重发通知，直到超过24小时22分钟。一般情况下，25小时以内完成8次通知（通知的间隔频率一般是：4m,10m,10m,1h,2h,6h,15h）

	jStr, err := json.Marshal(notifyReq)
	if err != nil {
		fmt.Printf("支付回调参数转json异常:%s", err.Error())
	}
	return notifyReq.GetString("out_trade_no"), string(jStr), nil
	// return c.SendString("fail")
	// return c.SendString("success")
}

// PayByH5 H5支付（返回支付链接）
func (m *Ali) PayByH5(subject, outTradeNo, totalAmount string) (string, error) {
	//请求参数
	bm := gopay.BodyMap{}
	bm.Set("subject", subject)
	bm.Set("out_trade_no", outTradeNo)
	bm.Set("quit_url", "https://www.fmm.ink")
	bm.Set("total_amount", totalAmount)
	bm.Set("product_code", "QUICK_WAP_WAY")
	return AliCli.TradeWapPay(context.Background(), bm)
}

// PayByWebTest H5支付（返回支付链接）
func (m *Ali) PayByH5Test(c *fiber.Ctx) error {
	//请求参数
	bm := gopay.BodyMap{}
	bm.Set("subject", "手机网站测试支付")
	bm.Set("out_trade_no", outTradeNo)
	bm.Set("quit_url", "https://www.fmm.ink")
	bm.Set("total_amount", "1.00")
	bm.Set("product_code", "QUICK_WAP_WAY")

	//手机网站支付请求,返回支付链接
	payUrl, err := AliCli.TradeWapPay(context.Background(), bm)
	if err != nil {
		return err
	}
	log.Println(payUrl, err)
	return c.SendString(payUrl)
}

// PayByAppTest APP支付（返回加签的订单）
func (m *Ali) PayByAppTest(c *fiber.Ctx) error {
	//请求参数
	bm := gopay.BodyMap{}
	bm.Set("subject", "测试APP支付")
	bm.Set("out_trade_no", outTradeNo)
	bm.Set("total_amount", "1.00")
	//手机APP支付参数请求
	AliRsp, err := AliCli.TradeAppPay(context.Background(), bm)
	if err != nil {
		return err
	}
	log.Println(AliRsp, err)
	return c.SendString(AliRsp)
}

// PayByWebTest web支付（支付宝支付二维码页面）
func (m *Ali) PayByWebTest(c *fiber.Ctx) error {
	//请求参数
	bm := gopay.BodyMap{}
	bm.Set("subject", "网站测试支付")
	bm.Set("out_trade_no", outTradeNo)
	bm.Set("total_amount", "1.00")
	bm.Set("product_code", "FAST_INSTANT_TRADE_PAY")

	//电脑网站支付请求
	payUrl, err := AliCli.TradeWapPay(context.Background(), bm)
	if err != nil {
		return err
	}
	log.Println(payUrl, err)
	return c.SendString(payUrl)
}

// Query 查询
func (m *Ali) Query(outTradeNo string) error {
	//请求参数
	bm := gopay.BodyMap{}
	bm.Set("out_trade_no", outTradeNo)
	AliRsp, err := AliCli.TradeQuery(context.Background(), bm)
	if err != nil {
		return err
	}
	bytes, _ := json.Marshal(AliRsp)
	str := string(bytes)
	log.Printf("AliRsp: %s", str)
	return nil
}

// QueryTest 查询
func (m *Ali) QueryTest(c *fiber.Ctx) error {
	//请求参数
	bm := gopay.BodyMap{}
	bm.Set("out_trade_no", outTradeNo)
	AliRsp, err := AliCli.TradeQuery(context.Background(), bm)
	if err != nil {
		return err
	}
	bytes, _ := json.Marshal(AliRsp)
	str := string(bytes)
	log.Printf("AliRsp: %s", str)
	return c.SendString(str)
}

// CloseTest 关闭
func (m *Ali) CloseTest(c *fiber.Ctx) error {
	bm := gopay.BodyMap{}
	bm.Set("out_trade_no", outTradeNo)
	AliRsp, err := AliCli.TradeClose(context.Background(), bm)
	if err != nil {
		return err
	}
	bytes, _ := json.Marshal(AliRsp)
	str := string(bytes)
	log.Printf("AliRsp: %s", str)
	return c.SendString(str)
}

// CancelTest 撤销
func (m *Ali) CancelTest(c *fiber.Ctx) error {
	bm := gopay.BodyMap{}
	bm.Set("out_trade_no", outTradeNo)
	AliRsp, err := AliCli.TradeCancel(context.Background(), bm)
	if err != nil {
		return err
	}
	bytes, _ := json.Marshal(AliRsp)
	str := string(bytes)
	log.Printf("AliRsp: %s", str)
	return c.SendString(str)
}

// RefundTest 退款
func (m *Ali) RefundTest(c *fiber.Ctx) error {
	bm := gopay.BodyMap{}
	bm.Set("out_trade_no", "GZ201907301420334577")
	bm.Set("refund_amount", "5")
	bm.Set("refund_reason", "测试退款")
	AliRsp, err := AliCli.TradeRefund(context.Background(), bm)
	if err != nil {
		return err
	}
	bytes, _ := json.Marshal(AliRsp)
	str := string(bytes)
	log.Printf("AliRsp: %s", str)
	return c.SendString(str)
}

// RefundQueryTest 退款查询
func (m *Ali) RefundQueryTest(c *fiber.Ctx) error {
	bm := gopay.BodyMap{}
	bm.Set("out_trade_no", "GZ201907301420334577")
	bm.Set("out_request_no", "GZ201907301420334577")
	AliRsp, err := AliCli.TradeFastPayRefundQuery(context.Background(), bm)
	if err != nil {
		return err
	}
	bytes, _ := json.Marshal(AliRsp)
	str := string(bytes)
	log.Printf("AliRsp: %s", str)
	return c.SendString(str)
}
